package org.lili.util.date;

/**
 * 自实现日期计算
 * 
 * @author lili
 * 
 */
public class CalDate {
	static int[] daysOfMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	/**
	 * 计算两日期相隔天数
	 * 
	 * @param ys
	 * @param ms
	 * @param ds
	 * @param ye
	 * @param me
	 * @param de
	 * @return
	 */
	public static int CalculateDays(int ys, int ms, int ds, int ye, int me,
			int de) {
		int days = CalcYearRestDays(ys, ms, ds);
		if (ys != ye) /* 不是同一年的日期 */
		{
			if ((ye - ys) >= 2) /* 间隔超过一年，要计算间隔的整年时间 */
			{
				days += CalcYearsDays(ys + 1, ye);
			}
			days += CalcYearPassedDays(ye, me, de);
		} else {
			days = days - CalcYearRestDays(ye, me, de);
		}
		return days;
	}

	/**
	 * 计算一年中过去的天数，包括指定的这一天
	 * 
	 * @param year
	 * @param month
	 * @param day
	 * @return
	 */
	static int CalcYearPassedDays(int year, int month, int day) {
		int passedDays = 0;
		int i;
		for (i = 0; i < month - 1; i++) {
			passedDays += daysOfMonth[i];
		}
		passedDays += day;
		if ((month > 2) && IsLeapYear(year))
			passedDays++;
		return passedDays;
	}

	/**
	 * 判断是否为闰年
	 * 
	 * @param year
	 * @return
	 */
	static boolean IsLeapYear(int year) {
		if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
			return true;
		return false;
	}

	/**
	 * 计算一年中还剩下的天数，不包括指定的这一天
	 * 
	 * @param year
	 * @param month
	 * @param day
	 * @return
	 */
	static int CalcYearRestDays(int year, int month, int day) {
		int leftDays = daysOfMonth[month - 1] - day;
		int i;
		for (i = month; i < 12; i++) {
			leftDays += daysOfMonth[i];
		}
		if ((month <= 2) && IsLeapYear(year))
			leftDays++;
		return leftDays;
	}

	/**
	 * 计算years年1月1日和yeare年1月1日之间的天数， 包括years年1月1日，但是不包括yeare年1月1日
	 * 
	 * @param years
	 * @param yeare
	 * @return
	 */
	static int CalcYearsDays(int years, int yeare) {
		int days = 0;
		int i;
		for (i = years; i < yeare; i++) {
			if (IsLeapYear(i))
				days += 366;
			else
				days += 365;
		}
		return days;
	}

	/**
	 * 计算星期几
	 * 
	 * 公式：蔡勒（Zeller）公式
	 * 
	 * 
	 * 对计算出的w值除以7，得到的余数就是星期几，如果余数是0，则为星期日。蔡勒公式中各符号的含义如下：
	 * 
	 * w ：星期；
	 * 
	 * c ：世纪数 – 1的值，如21世纪，则 = 20；
	 * 
	 * m ：月数，的取值是大于等于3，小于等于14。在蔡勒公式中，某年的1月和2月看作上一年的13月和14月，
	 * 比如2001年2月1日要当成2000年的14月1日计算；
	 * 
	 * y ：年份，取公元纪念的后两位，如1998年， = 98，2001年， = 1；
	 * 
	 * d ：某月内的日数
	 * 
	 * @param year
	 * @param month
	 * @param day
	 * @return
	 */
	public static int calWeek(int year, int month, int day) {
		int m = month;
		int d = day;
		if (month <= 2) /* 对小于2的月份进行修正 */
		{
			year--;
			m = month + 12;
		}
		int y = year % 100;
		int c = year / 100;
		int w = (y + y / 4 + c / 4 - 2 * c + (13 * (m + 1) / 5) + d - 1) % 7;
		if (w < 0) /* 修正计算结果是负数的情况 */
			w += 7;
		return w;
	}

	/**
	 * 打印日历
	 * 
	 * @param year
	 * @param month
	 */
	public static void PrintMonthCalendar(int year, int month) {
		int days = getDaysOfMonth(year, month); /* 确定这个月的天数 */
		if (days <= 0)
			return;
		System.out.println(nameOfMonth[month - 1]);
		PrintWeekBanner();
		int firstDayWeek = calWeek(year, month, 1);
		// InsertRowSpace(firstDayWeek);
		// for (int i = 0; i < firstDayWeek; i++) {
		System.out.print("		");
		// }
		int week = firstDayWeek;
		int i = 1;
		while (i <= days) {
			System.out.print(String.format("%-10d", i));
			if (week == 6) /* 到一周结束，切换到下一行输出 */
			{
				// SetNextRowStart();
				System.out.println("");
			}
			i++;
			// 计算星期不必对每一天都计算一次，只要对每个月的第一天计算一次就可以了，以后的日期可以用 week = (week + 1) % 7
			// 直接推算出星期几。
			week = (week + 1) % 7;
		}
	}

	private static void PrintWeekBanner() {
		System.out.println("星期一	星期二	星期三	星期四	星期五	星期六	星期日");
	}

	public static final String[] nameOfMonth = { "一月", "二月", "三月", "四月", "五月",
			"六月", "七月", "八月", "九月", "十月", "十一月", "十二月" };

	public static int getDaysOfMonth(int year, int month) {
		int days = daysOfMonth[month - 1];
		if ((month == 2) && IsLeapYear(year))
			days++;
		return days;
	}

	public static void main(String[] args) {
		// System.out.println(CalDate.calWeek(2012, 8, 5));
		PrintMonthCalendar(2012, 8);
	}
}
